 
  

 






<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=221 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
<head>
 <title>
  Java Practices -> Wrap file upload requests
 </title>
 <link rel="stylesheet" type="text/css" href="../stylesheet8.css" media="all">
 
 <link rel="shortcut icon" href='../images/favicon.ico' type="image/vnd.microsoft.icon">
 <meta name="description" content="Concise presentations of java programming practices, tasks, and conventions, amply illustrated with syntax highlighted code examples.">
 
 <meta name='keywords' content='java,java programming,java practices,java idiom,java style,java design patterns,java coding conventions,'>
 
 
</head>
 
<body>


<div class='menu-bar'>
 
  <a href='../home/HomeAction.html' title='Table of Contents'>Home</a> |
  <a href='../vote/VoteSummaryAction-2.html' title='View Poll Results'>Poll</a> |
   
  <A href='../feedback/FeedbackAction451f-2.html?Operation=Show' title='Send Your Feedback'>Wiki</a> |
  <b><a href='../source/SourceAction-2.html' title='Grab Source Code'>Source Code</a></b><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |

  <a href='http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp?From=1' title='Free Download - Java Web Application Framework'><b>WEB4J</b></a> |
  
  <a href='http://www.date4j.net/' title='Replacement for java.util.Date'><b>DATE4J</b></a> |

   <a href='../references/ReferencesAction-2.html' title='References'>Links</a>
   
  <form action='http://www.javapractices.com/search/SearchAction.do' method='get' class='search-form'>
   <input type='text' name='SearchTerms' value="" size=12 maxlength=50 class='search'>
   <input type='submit' value="Search">
  </form>
 
</div>

<P>



  

 






<p class="display-messages">

 

 

</p>


<div class="main-layout">
 
   

 




<div class='page-title'>Wrap file upload requests</div>

<div class='main-body'>
 <br>
The Servlet API has poor support for file uploads. 
If a form contains one or more <a href='http://www.w3.org/TR/html4/interact/forms.html#h-17.4'>file upload</a> controls, the format of the underlying HTTP request changes dramatically. 
Unless special steps are taken, it's likely that <tt>request.<a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/ServletRequest.html#getParameter(java.lang.String)" >getParameter</a>(String)</tt> will return <tt>null</tt>
for <em>all</em> parameters (both regular parameters and file upload parameters).

<P>Reminder : if a form includes a file upload control, then it must have :
<ul>
<li><tt>method='POST'</tt>
<li><tt>enctype='multipart/form-data'</tt>
</ul>

<P>One technique for handling file upload requests uses a <em>wrapper</em> for the underlying request, such that 
<tt>request.getParameter(String)</tt> and related methods may be used in the usual way.

<P><b>Example</b>
<P>The following wrapper uses the Apache Commons <a href='http://commons.apache.org/fileupload/'>FileUpload</a>
tool to parse the request into both regular parameters and file upload parameters. 
An action class may use this class as it would any request, with one exception: to access the methods related specifically to files, a cast is necessary.
<br>
<PRE>

<span class='keyword'>import</span> java.util.*;
<span class='keyword'>import</span> java.io.*;
<span class='keyword'>import</span> javax.servlet.http.HttpServletRequestWrapper;
<span class='keyword'>import</span> javax.servlet.http.HttpServletRequest;
<span class='keyword'>import</span> org.apache.commons.fileupload.FileUploadException;
<span class='keyword'>import</span> org.apache.commons.fileupload.servlet.ServletFileUpload;
<span class='keyword'>import</span> org.apache.commons.fileupload.disk.DiskFileItemFactory;
<span class='keyword'>import</span> org.apache.commons.fileupload.FileItem;

<span class='comment'>/**
* Wrapper for a file upload request.
* 
* &lt;P&gt;This class uses the Apache Commons 
* &lt;a href='http://commons.apache.org/fileupload/'&gt;File Upload tool&lt;/a&gt;.
* The generous Apache License will very likely allow you to use it in your 
* applications as well. 
*/</span>
<span class='keyword'>public</span> <span class='keyword'>class</span> FileUploadWrapper <span class='keyword'>extends</span> HttpServletRequestWrapper {
  
  <span class='comment'>/** Constructor.  */</span>
  <span class='keyword'>public</span> FileUploadWrapper(HttpServletRequest aRequest) <span class='keyword'>throws</span> IOException {
    <span class='keyword'>super</span>(aRequest);
    ServletFileUpload upload = <span class='keyword'>new</span> ServletFileUpload( <span class='keyword'>new</span> DiskFileItemFactory());
    <span class='keyword'>try</span> {
      List&lt;FileItem&gt; fileItems = upload.parseRequest(aRequest);
      convertToMaps(fileItems);
    }
    <span class='keyword'>catch</span>(FileUploadException ex){
      <span class='keyword'>throw</span> <span class='keyword'>new</span> IOException(<span class='literal'>"Cannot parse underlying request: "</span> + ex.toString());
    }
  }
  
  <span class='comment'>/**
  * Return all request parameter names, for both regular controls and file upload 
  * controls.
  */</span>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> Enumeration getParameterNames() {
    Set&lt;String&gt; allNames = <span class='keyword'>new</span> LinkedHashSet&lt;String&gt;();
    allNames.addAll(fRegularParams.keySet());
    allNames.addAll(fFileParams.keySet());
    <span class='keyword'>return</span> Collections.enumeration(allNames);
  }
  
  <span class='comment'>/**
  * Return the parameter value. Applies only to regular parameters, not to 
  * file upload parameters. 
  * 
  * &lt;P&gt;If the parameter is not present in the underlying request, 
  * then &lt;tt&gt;null&lt;/tt&gt; is returned.
  * &lt;P&gt;If the parameter is present, but has no  associated value, 
  * then an empty string is returned.
  * &lt;P&gt;If the parameter is multivalued, return the first value that 
  * appears in the request.  
  */</span>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> String getParameter(String aName) {
    String result = <span class='keyword'>null</span>;
    List&lt;String&gt; values = fRegularParams.get(aName);
    <span class='keyword'>if</span>( values == <span class='keyword'>null</span> ){
      <span class='comment'>//you might try the wrappee, to see if it has a value 
</span>    }
    <span class='keyword'>else</span> <span class='keyword'>if</span> ( values.isEmpty() ) {
      <span class='comment'>//param name known, but no values present
</span>      result = <span class='literal'>""</span>;
    }
    <span class='keyword'>else</span> {
      <span class='comment'>//return first value in list
</span>      result = values.get(FIRST_VALUE);
    }
    <span class='keyword'>return</span> result;
  }
  
  <span class='comment'>/**
  * Return the parameter values. Applies only to regular parameters, 
  * not to file upload parameters.
  */</span>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> String[] getParameterValues(String aName) {
    String[] result = <span class='keyword'>null</span>;
    List&lt;String&gt; values = fRegularParams.get(aName);
    <span class='keyword'>if</span>( values != <span class='keyword'>null</span> ) {
      result = values.toArray(<span class='keyword'>new</span> String[values.size()]);
    }
    <span class='keyword'>return</span> result;
  }
  
  <span class='comment'>/**
  * Return a {@code Map&lt;String, String&gt;} for all regular parameters.
  * Does not return any file upload paramters at all. 
  */</span>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> Map getParameterMap() {
    <span class='keyword'>return</span> Collections.unmodifiableMap(fRegularParams);
  }
  
  <span class='comment'>/**
  * Return a {@code List&lt;FileItem&gt;}, in the same order as they appear
  *  in the underlying request.
  */</span>
  <span class='keyword'>public</span> List&lt;FileItem&gt; getFileItems(){
    <span class='keyword'>return</span> <span class='keyword'>new</span> ArrayList&lt;FileItem&gt;(fFileParams.values());
  }
  
  <span class='comment'>/**
  * Return the {@link FileItem} of the given name.
  * &lt;P&gt;If the name is unknown, then return &lt;tt&gt;null&lt;/tt&gt;.
  */</span>
  <span class='keyword'>public</span> FileItem getFileItem(String aFieldName){
    <span class='keyword'>return</span> fFileParams.get(aFieldName);
  }
  
  
  <span class='comment'>// PRIVATE //
</span>  
  <span class='comment'>/** Store regular params only. May be multivalued (hence the List).  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> Map&lt;String, List&lt;String&gt;&gt; fRegularParams = 
    <span class='keyword'>new</span> LinkedHashMap&lt;String, List&lt;String&gt;&gt;()
  ;
  <span class='comment'>/** Store file params only. */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> Map&lt;String, FileItem&gt; fFileParams = 
    <span class='keyword'>new</span> LinkedHashMap&lt;String, FileItem&gt;()
  ;
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> FIRST_VALUE = <span class='literal'>0</span>;
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> convertToMaps(List&lt;FileItem&gt; aFileItems){
    <span class='keyword'>for</span>(FileItem item: aFileItems) {
      <span class='keyword'>if</span> ( isFileUploadField(item) ) {
        fFileParams.put(item.getFieldName(), item);
      }
      <span class='keyword'>else</span> {
        <span class='keyword'>if</span>( alreadyHasValue(item) ){
          addMultivaluedItem(item);
        }
        <span class='keyword'>else</span> {
          addSingleValueItem(item);
        }
      }
    }
  }
  
  <span class='keyword'>private</span> <span class='keyword'>boolean</span> isFileUploadField(FileItem aFileItem){
    <span class='keyword'>return</span> ! aFileItem.isFormField();
  }
  
  <span class='keyword'>private</span> <span class='keyword'>boolean</span> alreadyHasValue(FileItem aItem){
    <span class='keyword'>return</span> fRegularParams.get(aItem.getFieldName()) != <span class='keyword'>null</span>;
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> addSingleValueItem(FileItem aItem){
    List&lt;String&gt; list = <span class='keyword'>new</span> ArrayList&lt;String&gt;();
    list.add(aItem.getString());
    fRegularParams.put(aItem.getFieldName(), list);
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> addMultivaluedItem(FileItem aItem){
    List&lt;String&gt; values = fRegularParams.get(aItem.getFieldName());
    values.add(aItem.getString());
  }
} 
</PRE>
<br>
The wrapper is in turn used by a <tt>Filter</tt> :
<br>
<PRE>

<span class='keyword'>import</span> java.io.*;
<span class='keyword'>import</span> javax.servlet.Filter;
<span class='keyword'>import</span> javax.servlet.FilterChain;
<span class='keyword'>import</span> javax.servlet.FilterConfig;
<span class='keyword'>import</span> javax.servlet.ServletException;
<span class='keyword'>import</span> javax.servlet.ServletRequest;
<span class='keyword'>import</span> javax.servlet.ServletResponse;
<span class='keyword'>import</span> javax.servlet.http.HttpServletRequest;

<span class='comment'>/**
* Filter that wraps an underlying file upload request. 
* 
* &lt;P&gt;This filter should be configured only for those operations that use a 
* file upload request.
*/</span>
<span class='keyword'>public</span> <span class='keyword'>final</span> <span class='keyword'>class</span> FileUploadFilter <span class='keyword'>implements</span> Filter {

  <span class='keyword'>public</span> <span class='keyword'>void</span> init(FilterConfig aConfig) <span class='keyword'>throws</span> ServletException {
    <span class='comment'>//do nothing
</span>  }
  
  <span class='keyword'>public</span> <span class='keyword'>void</span> destroy() {
    <span class='comment'>//do nothing
</span>  }
  
  <span class='keyword'>public</span> <span class='keyword'>void</span> doFilter(
   ServletRequest aRequest, ServletResponse aResponse, FilterChain aChain
  ) <span class='keyword'>throws</span> IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) aRequest;
    <span class='keyword'>if</span> ( isFileUploadRequest(request) ) {
      FileUploadWrapper wrapper = <span class='keyword'>new</span> FileUploadWrapper(request);
      aChain.doFilter(wrapper, aResponse);
    }
    <span class='keyword'>else</span> {
      aChain.doFilter(aRequest, aResponse);
    }
  }
  
  <span class='keyword'>private</span> <span class='keyword'>boolean</span> isFileUploadRequest(HttpServletRequest aRequest){
    <span class='keyword'>return</span>     
      aRequest.getMethod().equalsIgnoreCase(<span class='literal'>"POST"</span>) &amp;&amp; 
      aRequest.getContentType().startsWith(<span class='literal'>"multipart/form-data"</span>)
    ;
  }
}
 
</PRE>
<br>
The <tt>Filter</tt> is configured in <tt>web.xml</tt> to handle specific URLs :
<PRE>
&lt;web-app&gt;
  &lt;filter&gt;
   &lt;filter-name&gt;FileUpload&lt;/filter-name&gt;
   &lt;display-name&gt;File Upload&lt;/display-name&gt;
   &lt;description&gt;
     Applied to file upload requests.
     Wraps the request, allowing it to be used as a regular request, 
     'as if' it were parsed by the Servlet API.
   &lt;/description&gt;
   &lt;filter-class&gt;com.blah.FileUploadFilter&lt;/filter-class&gt;
  &lt;/filter&gt;

  &lt;filter-mapping&gt;
   &lt;filter-name&gt;FileUpload&lt;/filter-name&gt;
   &lt;url-pattern&gt;/someurl/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;

  &lt;!-- Required by the Apache FileUpload tool. --&gt;
  &lt;listener&gt;
    &lt;listener-class&gt;
     org.apache.commons.fileupload.servlet.FileCleanerCleanup
    &lt;/listener-class&gt;
  &lt;/listener&gt; 
&lt;/web-app&gt;
</PRE>

<P>There's a variation regarding the <tt>request.<a href='http://java.sun.com/javaee/5/docs/api/javax/servlet/ServletRequest.html#getRequestDispatcher(java.lang.String)'>getDispatcher</a>(String)</tt> method which can be important here.
This method takes a path argument, and that path can contain query parameters <em>not present in the original request</em>.
Therefore, the above wrapper will not 'see' such query parameters unless special measures are taken.
For example, <tt>getParameter(String)</tt> might be modified along these lines:
<PRE>
@Override public String getParameter(String aName) {
  String result = null;
  List<String> values = fRegularParams.get(aName);
  if( values == null ){
    //see if the 'wrappee' has a value
    String superValue = super.getParameter(aName);
    if( Util.textHasContent(superValue)) {
      result = superValue;
    }
  }
  ...elided...
}
</PRE>

<P>Finally, for a second example of a file upload request wrapper, see this
<a href='http://www.javaworld.com/jw-06-2001/jw-0622-filters.html?page=5'>article</a> by Jason Hunter.
<br>
<br>

</div>




<div class='topic-section'>See Also :</div>
<div class='main-body'>
 
  
  <a href='TopicAction5422-2.html?Id=218'>Understand details of FORM tags</a> <br>
 
</div>


<div class='topic-section'>Would you use this technique?</div>
<div class='main-body'>
  
  <form action="http://www.javapractices.com/vote/AddVoteAction.do" method='post'>
    Yes<input type='radio' name='Choice' value='Y' >
    &nbsp;&nbsp;No<input type='radio' name='Choice' value='N'>
    &nbsp;&nbsp;Undecided<input type='radio' name='Choice' value="?" >
    &nbsp;&nbsp;<input type=submit value="Vote" >
    <input type='hidden' name='Operation' value='Apply'>
    <input type='hidden' name='TopicId' value='221'>
  </form>
</div>

<div style='height:10.0em;'></div>

 
 
</div>

  

 





<div align='center' class='legalese'>  
&copy; 2011 Hirondelle Systems |
<a href='../source/SourceAction-2.html'><b>Source Code</b></a><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |
<a href="mailto:webmaster@javapractices.com">Contact</a> |
<a href="http://creativecommons.org/licenses/by-nc-sa/1.0/">License</a> |
<a href='../apps/cjp.rss'>RSS</a>
<!-- ukey="2AC36CD2" -->
<!-- ckey="16DF3D87" -->
<br>

 Individual code snippets can be used under this <a href='../LICENSE.txt'>BSD license</a> - Last updated on June 6, 2010.<br>
 Over 150,000 unique IPs last month - <span title='Java Practices 2.6.5, Mon May 16 00:00:00 EDT 2011'>Built with</span> <a href='http://www.web4j.com/'>WEB4J</a>.<br>
 - In Memoriam : Bill Dirani -
</div>

<script src="../../www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2633428-1";
urchinTracker();
</script>



</body>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=221 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
</html>
